﻿package org.rkuk.kasutil.timeline 
{
	import flash.utils.Dictionary;
	import org.rkuk.kasutil.timeline.conditions.ITickerUpdateCondition;
	import org.rkuk.kasutil.timeline.ITicker;
	/**
	 * 用于按照一定时间间隔不断发出刷新信号以触发被注册的ITicker对象进行刷新.
	 * 实现了按照优先级顺序先后刷新被注册的ITicker对象，并实现了满足刷新条件时才进行ITicker对象的刷新。
	 * @author 		rkuk
	 * @see	org.rkuk.kasutil.timeline.Clock
	 * @see	org.rkuk.kasutil.timeline.ITicker
	 * @see	org.rkuk.kasutil.timeline.conditions.ITickerUpdateCondition
	 */
	public class BasicTimeline extends Timeline
	{
		private var _clock:Clock;
		/**
		 * 注册的ITicker对象会储存在此，以供轮询的时候使用.
		 * @see		#loopUpdateTicker()
		 */
		protected var _updateList:Vector.<ITicker>;
		
		/**
		 * ITicker对象注册时的配置储存在此.
		 * 将ITicker对象本身为key，可以获得相应的配置对象。
		 */
		protected var _settings:Dictionary;
		
		/**
		 * 构造函数
		 * @param	clock	时间线使用的Clock对象.
		 * 时间线通过征听Clock发出的ClockEvent.Ticker事件来定时触发刷新操作。
		 * 通过使用不同的Clock可以实现按帧刷新或按时间刷新，即使用不同的Clock时，刷新时间的单位不同。
		 * @see		org.rkuk.kasutil.timeline.Clock
		 * @see		org.rkuk.kasutil.timeline.ClockEvent
		 */
		public function BasicTimeline(clock:Clock) 
		{
			super();
			init(clock);
		}
		
		private function init(clock:Clock):void
		{
			_clock = clock;
			_updateList = new Vector.<ITicker>();
			_settings = new Dictionary(true);
			
			//监测clock发出的ClockEvent.TICK事件
			_clock.addEventListener(ClockEvent.TICK, loopUpdateTicker);
		}
		
		/**
	     * 将ITicker对象注册到Timeline上，让ITicker对象能够接收到Timeline发出的刷新信号以执行相应的操作.
		 * 注册时会根据优先级对所有注册对象重新排序，以让优先级高的对象先刷新。同时可以设定刷新条件。
	     * @param ticker	被添注册的ITicker对象
	     * @param setting	ITicker对象被注册时的配置.
		 * <li>通过setting.priority属性可以指定被注册的ITicker对象的刷新优先级，优先级是一个整数，数值越大优先级越高，刷新时越靠前</li>
		 * <li>通过setting.condition属性可以指定刷新条件，仅当刷新条件满足时，ITicker对象才被刷新，刷新条件是一个ITickerUpdateCondition对象</li>
		 * @return	添加成功则返回true，否则返回false
		 * @see		org.rkuk.kasutil.timeline.conditions.ITickerUpdateCondition
	     */
		override public function register(ticker:ITicker, setting:Object = null):Boolean 
		{
			if (ticker == null)
				return false;
				
			if (_updateList.indexOf(ticker) == -1)
			{
				_updateList.push(ticker);
				_settings[ticker] = setting;
				
				if (setting != null && "priority" in setting && _updateList.length > 1)
					_updateList.sort(sortUpdateList);
					
				return true;
			}
			
			return false;
		}
		
		/**
		 * @inheritDoc
		 */
		override public function unregister(ticker:ITicker):Boolean 
		{
			if (ticker != null)
			{
				var index:int = _updateList.indexOf(ticker);
				if (index != -1)
				{
					_updateList.splice(index, 1);
					delete _settings[ticker];
					return true;
				}
			}
			
			return false;
		}
		
		/**
		 * 根据ITicker注册时的配置指定的优先级，对所有注册的ITicker对象进行排序的函数.
		 * 排序时使用了register方法中setting参数的priority属性。
		 * @param	ticker1		参加比较的一个ITicker对象
		 * @param	ticker2		参加比较的另一个ITicker对象
		 * @return	参见Vector.sort方法使用的compare函数的返回值
		 * @see		#register()
		 */
		protected function sortUpdateList(ticker1:ITicker, ticker2:ITicker):int
		{
			var p1:Number = _settings[ticker1] == null ? NaN : _settings[ticker1].priority;
			var p2:Number = _settings[ticker2] == null ? NaN : _settings[ticker2].priority;
			
			if (!isNaN(p1) && !isNaN(p2))
				return p2 - p1;
			else if (isNaN(p1) && isNaN(p2))
				return 0;
			else if (isNaN(p1))
				return 1;
			else
				return -1;
		}
		
		/**
		 * 响应Clock发出的ClockEvent.TICK事件，实现轮询所有注册的ITicker并让其刷新.
		 * 这里主要完成轮询所有注册的ITicker对象，并通过updateTicker方法让ITicker对象刷新，
		 * 重载这个方法可以改变轮询所有注册的ITicker对象的顺序，默认时是按照ITicker对象被
		 * 注册的先后顺序以及注册设置的优先级轮询。
		 * @param	e	Clock发出的ClockEvent.TICK事件
		 * @see		#updateTicker()
		 * @see		#register()
		 */
		protected function loopUpdateTicker(e:ClockEvent):void
		{
			//_currentTimeStep = e.currentTimeStep;
			if (_updateList.length < 1)
				return;
				
			for (var i:int = 0; i < _updateList.length; i++)
				updateTicker(_updateList[i], e);
		}
		
		/**
		 * 实现刷新每个注册的ITicker对象，也就是调用ITicker对象的updateTicker方法.
		 * 在调用ITicker对象的updateTicker方法之前，会判断注册时配置的刷新条件条件是否满足，
		 * 仅当刷新条件满足时，对应的ITicker对象才会被刷新。ITicker对象的刷新条件是通过register
		 * 方法中setting参数的condition属性指定。
		 * @param	ticker		被刷新的ITicker对象，其updateTicker方法会在此被调用。
		 * @param	tickInfo	timeline发出信号时的时间.
		 * 根据timeline使用的Clock的类型，时间单位会不同。
		 * @see		org.rkuk.kasutil.timeline.conditions.ITickerUpdateCondition
		 * @see		org.rkuk.kasutil.timeline.ITickInfo
		 * @see		#register()
		 */
		protected function updateTicker(ticker:ITicker, tickInfo:ITickInfo):void
		{
			var setting:Object = _settings[ticker];
			if (setting != null && "condition" in setting)
			{
				var condition:ITickerUpdateCondition = setting["condition"] as ITickerUpdateCondition;
				if (condition != null && !condition.isNeedUpdate(this,ticker,tickInfo))
					return;//执行条件不满足，因此不执行ticker.updateTicker方法
			}
			
			ticker.updateTicker(tickInfo);
		}
		
		/**
		 * @inheritDoc
		 * @see		org.rkuk.kasutil.timeline.Clock
		 */
		override public function get currentTime():int 
		{
			return _clock.currentTime;
		}
		
		/**
		 * @inheritDoc
		 * @see		org.rkuk.kasutil.timeline.Clock
		 */
		override public function get currentTimeStep():int
		{
			return _clock.currentTimeStep;
		}
		
		/**
		 * @inheritDoc
		 */
		override public function get isPlaying():Boolean
		{
			return _clock.isStarted;
		}
		
		/**
		 * @inheritDoc
		 */
		override public function play(immediately:Boolean = false):void 
		{
			_clock.start(immediately);
		}
		
		/**
		 * @inheritDoc
		 */
		override public function stop():void 
		{
			_clock.stop();
		}
		
		/**
		 * @inheritDoc
		 */
		override public function get numTickers():int
		{
			return _updateList.length;
		}
	}
}